home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / news / misc / eep / eep1_9 / eepmain.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-03-21  |  33.0 KB  |  1,040 lines

  1. /*------------------------------------------------------------------------
  2.        Name: eepmain.c
  3.     Version: 1.9
  4.  
  5. This program makes it easier for users to change their .newsrc file.  
  6. It uses curses to implement a simple editor, which allows users to 
  7. join or unjoin newsgroups, from outside of rn or trn.
  8.  
  9.      Author: Paul Gillingwater, paul@actrix.co.at
  10.  
  11. Usage:
  12.     eep [-d] [-n] [-p] [-v]
  13.  
  14. Options:
  15.     -d:  Delete unsubscribed newsgroups
  16.     -n:  Disable NNTP connection
  17.     -p:  Use a terse pointer on screen instead of a bar
  18.     -v:  Verbose mode, tells you more during startup
  19.  
  20. ------------------------------------------------------------------------*/
  21.  
  22. #include <stdio.h>
  23. #include <string.h>
  24. #include <ctype.h>
  25. #include <signal.h>
  26.  
  27. #ifdef UNIX
  28. #include <fcntl.h>
  29. #include <termio.h>  /* Some version of curses.h will include this
  30.                       * automatically, leading to duplication.  */
  31. #endif /* UNIX */
  32.  
  33. #ifdef ANSI
  34. #include <stdlib.h>
  35. #else
  36. #define void int
  37. extern char *malloc();
  38. extern char *getenv();
  39. #endif /* ANSI */
  40.  
  41. #include "eep.h"
  42.  
  43. /* Some function definitions */
  44.  
  45. extern void    newsmain();
  46. extern void    *wallop();  /* eepmisc.c -- malloc() replacement */
  47. int            read_active_file();  /* forward reference */
  48. #ifdef NNTP
  49. int            read_active_nntp();  /* forward reference */
  50. #endif /* NNTP */
  51.  
  52. char    buffer[BUFSIZE], /* general purpose line buffer */
  53.         tmp[BUFSIZE],
  54.         s_hi[30],       /* strings for numbers */
  55.         s_lo[30],       /* strings for numbers */
  56.         s_flag[8];
  57. char    t_status,       /* from .newsrc file */
  58.         t_flag;         /* from active file */
  59. char    *ptr;           /* general purpose pointer */
  60. char    *bog_msg = "Bogus newsgroup (not in active file)";
  61. char    *home = '\0';    /* home directory */
  62.  
  63. unsigned int     
  64.         uid = 0,        /* real user id */
  65.         pid = 0;        /* process id */
  66.  
  67. int     result;                 /* flag for searching */
  68. int     eepoint = 0;            /* flag for screen pointer type */
  69. int     curses_ok = FALSE;      /* has curses been used yet? */
  70. int     verbose = VERBOSE;    /* verbose flag toggle */
  71. int     del_unsub = FALSE;      /* delete unsubscribed newsgroups? */
  72. int     warning = FALSE;        /* have warning messages been issued? */
  73.  
  74. char    *name,   /* pointers into line in active file */
  75.         *hi,
  76.         *lo,
  77.         *flag;
  78.  
  79. #ifdef NNTP
  80. int     nntp_ok = TRUE;
  81. #endif /* NNTP */
  82.  
  83. FILE    *fnewsrc,
  84.         *factive;
  85.  
  86. struct  actif *act[MAXLINES];  /* here's the main array */
  87. struct  actif *aptr;    /* temporary pointer */
  88.  
  89. /* To make malloc() faster, we use wallop() [see eepmisc.c] to
  90.  * handle memory.  We'll rely on wallop() to store the pointers
  91.  * so they can be freed during cleanup().  Wallop() will use
  92.  * buffer_city and malloc_count.
  93.  */
  94. char    *buffer_city[MAXBUFS];
  95. int     malloc_count = 0;
  96.  
  97. int     i_active,    /* index into actif arrays */
  98.         c_active,    /* number of elements in act array */
  99.         sub_count = 0,   /* count of subscribed newsgroups */
  100.         bog_count;   /* count of bogus newsgroups */
  101. int     high,low,mid;    /* used for binary chop searching */
  102.  
  103. /* The levels array contains the Head pointers which control the
  104. linked lists of newsgroups grouped by their "depth" in the
  105. hierarchy, and sorted alphabetically.  E.g. alt, sci, soc are 
  106. level 0, alt.pagan, sci.physics are level 1 and so on. */
  107.  
  108. struct actif *levels[MAXLEVELS];  /* keep track of levels with this array */
  109.  
  110. int    i_levels;    /* index into array */
  111.  
  112. /* Comparison functions for qsort.  These functions are used for
  113. sorting the array of pointers that point to our actif data structures. */
  114.  
  115. int    qcompare(item1,item2)    /* sort by name */
  116. struct actif    **item1, **item2;
  117. {
  118. struct actif    *ptr1, *ptr2;
  119.     ptr1 = (struct actif *) *item1;
  120.     ptr2 = (struct actif *) *item2;
  121.     return (strcmp(ptr1->name, ptr2->name));
  122. }
  123.  
  124. int    icompare(item1,item2)    /* sort by index number */
  125. struct actif    **item1, **item2;
  126. {
  127. struct actif    *ptr1, *ptr2;
  128.     ptr1 = (struct actif *) *item1;
  129.     ptr2 = (struct actif *) *item2;
  130.     return (ptr1->position - ptr2->position);
  131. }
  132.  
  133. /* This routine will move all subscribed newsgroups to the top
  134.  * of the list.  If the list was in alphabetical order, then 
  135.  * this order will be retained, otherwise numeric order will
  136.  * be used.
  137.  */
  138. int    scompare(item1,item2)    /* sort by subscription status */
  139. struct actif    **item1, **item2;
  140. {
  141. struct actif    *ptr1, *ptr2;
  142. int    temp_result;
  143. extern int alphabetized;
  144.  
  145.     ptr1 = (struct actif *) *item1;
  146.     ptr2 = (struct actif *) *item2;
  147.     temp_result = ptr2->status - ptr1->status;
  148.     if (temp_result == 0) {
  149.        if (alphabetized) 
  150.            return (strcmp(ptr1->name, ptr2->name));
  151.        else
  152.            return (ptr1->position - ptr2->position);
  153.     }
  154.     return (temp_result);
  155. }
  156.  
  157. /* cleanup() -- remove lock file, then exit. */
  158.  
  159. void    cleanup()
  160. {
  161. #ifdef UNIX
  162. extern struct termio tbufsave;
  163. extern int    tbuf_ok;
  164.  
  165.     if (tbuf_ok) ioctl(0, TCSETAF, &tbufsave);
  166. #endif /* UNIX */
  167.  
  168.     if (curses_ok) endwin();  /* terminate curses */
  169. #ifdef UNIX
  170.     if (home != (char *)NULL)
  171.         sprintf(tmp, "%s/%s", home, RNLOCK);
  172.     else    strcpy(tmp, RNLOCK);
  173.     if (verbose) printf("Removing lock file %s\n",tmp);
  174.     unlink(tmp);
  175. #endif /* UNIX */
  176.     /*
  177.      * Now run through the array containing pointers to the
  178.      * buffers allocated with malloc(), and free them.
  179.      */
  180.     if (verbose)
  181.         printf("Freeing %d buffers each of %d bytes.\n",
  182.             malloc_count,WALLOP_SIZE);
  183.     while (malloc_count > 0) {
  184.         free(buffer_city[malloc_count - 1]);
  185.         malloc_count--;
  186.     }
  187.     exit(0);
  188. }
  189.  
  190. /* This routine will read the a file (descfile) containing names and
  191.    descriptions of newsgroups, matching it with the active file.  */
  192.  
  193. void    read_desc(descfile)
  194. FILE    *descfile;
  195. {
  196. char    *name, *desc;  /* pointers to name and description */
  197. int     counter = 0;
  198.  
  199.     i_active = 0;      /* start with first one of course */
  200.     aptr = (struct actif *)NULL;
  201.  
  202.     while (fgets(buffer,BUFSIZE,descfile) != (char *)NULL) {
  203.  
  204.     /* ignore comment lines or blank lines */
  205.         switch(buffer[0]) {
  206.         case '#':
  207.         case '\n':
  208.         case '\r':
  209.         case '\0':
  210.             continue;
  211.         }
  212.  
  213.     /* Get name and description.  If either is absent, skip this line. */
  214.         if ((name = strtok(buffer, " \t\r\n")) == (char *)NULL) continue;
  215.         if ((desc = strtok((char *)NULL, "\r\n")) == (char *)NULL) continue;
  216.  
  217.     /* Advance over any whitespace preceding description */
  218.         while ((*(desc) == ' ') || (*(desc) == '\t')) desc++;
  219.  
  220.     /* Although we cannot assume that the newsgroups are in alphabetical
  221.      * order, we'll try looking at the next one anyway before doing a
  222.      * search. */
  223.  
  224.         if (i_active <= c_active)    /* range check */
  225.             aptr = act[i_active];
  226.  
  227.     /* This next line should never happen.  It would only occur if
  228.      * we've run out of things to match! */
  229.  
  230.         if (aptr == (struct actif *)NULL) {
  231.             if (verbose) printf("This should never happen!\n");
  232.             continue;
  233.         }
  234.  
  235.     /* Now compare the name read with current position in active file */
  236.  
  237.         if (strcmp(name,aptr->name) == 0) {
  238.  
  239. /* Here we look for the best description possible, i.e. one that is not 
  240. null, and preferably longest (i.e. not just a '?').  This will be because 
  241. we may find duplicate lines in the newsgroups file.  Also, it may be 
  242. possible to find no description at all. */
  243.  
  244.             if (aptr->desc != (char *)NULL) {
  245.             /* don't accept an inferior description */
  246.                 if (strlen(desc) <= strlen(aptr->desc)) continue;
  247.             }
  248.  
  249.         /* allocate space for string + null byte, then copy it in */
  250.             if ((aptr->desc = (char *)wallop(strlen(desc)+1)) == (char *)NULL) {
  251.                 printf("Fatal error while allocating memory!\n");
  252.                 cleanup();
  253.             }
  254.             strcpy(aptr->desc,desc);
  255.             counter++;
  256.             i_active++;      /* advance index to next pointer */
  257.             if (i_active == c_active) i_active = 0;
  258.             continue;
  259.         }
  260.  
  261.     /*  Here we begin a binary chop search, comparing the newsgroup
  262.      *  name we have just read from the newsgroups file with the list
  263.      *  of newsgroups read from the active file.  */
  264.  
  265.         low = 0;
  266.         high = c_active - 1;
  267. loop1:
  268.         if (low <= high) {
  269.             mid = (low+high)/2;
  270.             aptr = act[mid];
  271.             result = strcmp(name,aptr->name);
  272.             if (result == 0) {
  273.  
  274.                 if (aptr->desc != (char *)NULL) {
  275.                 /* don't accept an inferior description */
  276.                     if (strlen(desc) <= strlen(aptr->desc)) continue;
  277.                 }
  278.  
  279.             /* allocate space for string + null byte, then copy it in */
  280.                 if ((aptr->desc = (char *)wallop(strlen(desc)+1))
  281.                    == (char *)NULL) {
  282.                     printf("Fatal error while allocating memory!\n");
  283.                     cleanup();
  284.                 }
  285.                 strcpy(aptr->desc,desc);
  286.  
  287.                 i_active = mid + 1;
  288.                 if (i_active == c_active) i_active = 0;
  289.                 continue; /* with read loop */
  290.             } else
  291.             if (result > 0) { /* after */
  292.                 low = mid+1;
  293.                 goto loop1;
  294.             } else
  295.             if (result < 0) { /* before */
  296.                 high = mid-1;
  297.                 goto loop1;
  298.             }
  299.         }
  300.     }
  301.     fclose(descfile); 
  302.     if (verbose) printf("Found %d descriptions.\n",counter);
  303. }
  304.  
  305. /* Initialise the various chunks of memory and read in files. */
  306.  
  307. void    initial()
  308. {
  309.  
  310. int    newsrc_order;    /* track reading order from .newsrc */
  311. int    warning = FALSE; /* if warning messages have been issued */
  312.  
  313. char   *name,   /* pointers into line in active file */
  314.        *hi,
  315.        *lo,
  316.        *flag;
  317.  
  318. #ifdef UNIX
  319.  
  320.  
  321. /* Check for a lock file from rn or trn.  If it's not around,
  322.  * let's make our own lock file.
  323.  */
  324.     
  325.     if ((ptr = getenv("HOME"))   != (char *)NULL) home = ptr;
  326.     if ((ptr = getenv("DOTDIR")) != (char *)NULL) home = ptr;
  327.     if (home != (char *)NULL)
  328.         sprintf(tmp, "%s/%s", home, RNLOCK);
  329.     else    strcpy(tmp, RNLOCK);
  330.  
  331.     if ((factive = fopen(tmp, "r")) != (FILE *)NULL) { 
  332.        if (fgets(buffer,BUFSIZE,factive) != (char *)NULL) { 
  333.            pid = atoi(buffer); 
  334.            if (pid > 0) { 
  335.                if (kill(pid,0) >= 0) { /* doesn't exist? */ 
  336.                     printf("Lock file %s suggests process %ld is active.\n", 
  337.                     tmp,pid); 
  338.                     printf("Please run EEP without rn or trn running.\n");
  339.                     cleanup();
  340.                 } else if (verbose) {
  341.                     printf("A lock file for rn was found (%s)\n",tmp);
  342.                     printf("Process %ld not found -- file ignored.\n",pid);
  343.                 }
  344.             }
  345.         }
  346.     }
  347.     fclose(factive);
  348.  
  349.  /* Now create the lock file. */
  350.     pid = getpid();
  351.     if (verbose) printf("Creating lock file %s with pid %ld\n",tmp,pid);
  352.     if ((factive = fopen(tmp, "w")) == (FILE *)NULL) {
  353.         printf("Fatal: Can't create lock file %s \n", tmp);
  354.         exit(0);
  355.     }
  356.     fprintf(factive,"%ld\n", pid);
  357.     fclose(factive);
  358. #endif /* UNIX */
  359.  
  360. #ifdef DOS
  361.     msleep(0L);   /* initialize time routines */
  362. #endif /* DOS */
  363.  
  364. /*
  365.  * 
  366.  * Because NEWSGROUPS can often contain lots of duplicates, while
  367.  * the active file is "cleaner", let's read the active file FIRST,
  368.  * then scan the NEWSGROUPS and NEWSLOCAL file looking for
  369.  * descriptions.  We should try to find the best description, i.e.
  370.  * one that is not null, not a '?', and not "alt group".
  371.  * A simple sequential read of these files is adequate.  Note
  372.  * that the active file is authoritative when it comes to bogus
  373.  * groups, which means we could simply ignore entries from the
  374.  * NEWSGROUPS file that aren't in the active file.  
  375.  *
  376.  * When we read the .newsrc, we still need to add entries for
  377.  * bogus groups, to give people the option of keeping strange
  378.  * stuff in their .newsrc.  This means we should prompt before
  379.  * saving whether they want to lose the bogus groups or not.
  380.  * 
  381.  */
  382.  
  383. #ifdef NNTP
  384.     if (nntp_ok) {
  385.         read_active_nntp();
  386.     } else 
  387. #endif /* NNTP */
  388.         read_active_file();
  389.     
  390. /* Sort the list by newsgroup name. */
  391.     qsort( act, (unsigned) c_active,(unsigned) sizeof(act[0]), qcompare);
  392.  
  393. /* Now open NEWSGROUPS and NEWSLOCAL to look for descriptions.  If
  394.  * these files don't exist, it's no great tragedy -- but the whole
  395.  * point of EEP will be missed.  If you don't have them, encourage
  396.  * your news administrator to create them with the checkgroups script.
  397.  */
  398.      
  399. #ifdef NNTP
  400.     if (nntp_ok && read_desc_nntp() < 0) {
  401.         if ((factive = fopen(NEWSGROUPS, "r")) != (FILE *)NULL) {
  402.             if (verbose) printf("Reading descriptions from %s\n",NEWSGROUPS);
  403.             read_desc(factive);
  404.         }
  405.     }
  406. #else
  407.     if ((factive = fopen(NEWSGROUPS, "r")) != (FILE *)NULL) {
  408.         if (verbose) printf("Reading descriptions from %s\n",NEWSGROUPS);
  409.         read_desc(factive);
  410.     }
  411. #endif /* NNTP */
  412.  
  413.     if ((factive = fopen(NEWSLOCAL, "r")) != (FILE *)NULL) {
  414.         if (verbose) printf("Reading descriptions from %s\n",NEWSLOCAL);
  415.         read_desc(factive);
  416.     }
  417.  
  418.  /* Let's now build chains of pointers for each of the 
  419.     hierarchies of news groups, taking our initial pointer
  420.     from the array levels[]. */
  421.  
  422.     i_levels = 0;
  423.     while (i_levels < MAXLEVELS) 
  424.         levels[i_levels++] = (struct actif *) NULL; /* null array */
  425.  
  426.  /* Work backwards through the array, building the linked list.
  427.     We also record the array index in each record.  If we were
  428.     only accessing it as an array, this would be redundant, but
  429.     we're using multiple linked lists of pointers as well.  
  430.     We work backwards to ensure our chains are NULL terminated.  */
  431.  
  432.     i_active = c_active - 1;
  433.     while (i_active >= 0) {
  434.         aptr = act[i_active];
  435.         ptr = aptr->name;
  436.         i_levels = 0;
  437.         while ((ptr = strchr(ptr,'.')) != (char *)NULL) {
  438.             i_levels++;
  439.             ptr++;
  440.         }
  441.         if (i_levels < MAXLEVELS) {
  442.             aptr->depth = levels[i_levels];
  443.             levels[i_levels] = aptr;
  444.         }
  445.         i_active--;
  446.     }
  447.  
  448. /* This code will be re-used later when parsing newsgroups */
  449. /* let's check the pointers in levels[] */
  450. /*
  451.     i_levels = 0;
  452.     while (i_levels < MAXLEVELS) {
  453.         aptr = levels[i_levels];
  454.         while (aptr != (struct actif *)NULL) {
  455.             sprintf(tmp,"%-25s %-.67s\n",
  456.                 aptr->name,
  457.                 aptr->desc);
  458.             printf(tmp);
  459.             aptr = (struct actif *) aptr->depth;
  460.         }
  461.         i_levels++;
  462.     }
  463. */
  464.  
  465.     /* Now read in and match up our personal .newsrc */
  466.  
  467.     if (home != (char *)NULL)
  468.         sprintf(tmp, "%s/%s", home, NEWSRC);
  469.     else
  470.         sprintf(tmp, "%s", NEWSRC);   /* default to current directory */
  471.  
  472.     if ((fnewsrc = fopen(tmp, "r")) == (FILE *)NULL) {
  473.         printf("Fatal: Unable to read the file %s\n", tmp);
  474.         printf("Please create it using the rn or trn news reader.\n");
  475.         cleanup();
  476.     }
  477.  
  478.     i_active = 0;
  479.     newsrc_order = 1;
  480.     bog_count = 0;
  481.     if (verbose) printf("Now reading your personal news control file %s\n",tmp);
  482.     
  483.     while (fgets(buffer,BUFSIZE,fnewsrc) != (char *)NULL) {
  484.  
  485.     /* ignore comment lines */
  486.         switch(buffer[0]) {
  487.         case '#':
  488.         case '\n':
  489.         case '\r':
  490.         case '\0':
  491.             continue;
  492.         }
  493.  
  494.     /* strip off CR and LF */
  495.         while ((ptr = strchr(buffer,'\n')) != (char *)NULL) *ptr = '\0';
  496.         while ((ptr = strchr(buffer,'\r')) != (char *)NULL) *ptr = '\0';
  497.  
  498.     /* don't try to match a null string */
  499.         if (strlen(buffer) == 0) continue; 
  500.  
  501. /* Now examine the character that terminates the newsgroup name.
  502.    If it's a colon ':', then this means the newsgroup is active
  503.    for the user.  If it's an exclamation mark, then it is
  504.    inactive (unsubscribed).  Anything else means the
  505.    newsgroup may not be valid.  */
  506.  
  507.         if ((ptr = strchr(buffer,':')) != (char *)NULL) {
  508.             t_status = ':';
  509.             *ptr = '\0'; /* null terminate newsgroup */
  510.             ptr++;    /* point to rest of line (hilo) */
  511.         } else if ((ptr = strchr(buffer,'!')) != (char *)NULL) {
  512.             t_status = '!';
  513.             *ptr = '\0'; /* null terminate newsgroup */
  514.             ptr++;    /* point to rest of line (hilo) */
  515.         } else continue; /* not valid */
  516.  
  517.     /* advance past any whitespace */
  518.         while ((*ptr == ' ') || (*ptr == '\t')) ptr++;
  519.         strcpy(tmp,ptr);    /* this is hilo */
  520.  
  521.         aptr = act[i_active]; /* range check */
  522.         if ((aptr != (struct actif *)NULL) 
  523.             && (strcmp(buffer,aptr->name) == 0)) {
  524.             if (strlen(tmp) > 0) {
  525.                 if ((aptr->hilo = (char *)wallop(strlen(tmp)+1)) 
  526.                     == (char *)NULL) {
  527.                     printf("Error allocating memory!\n");
  528.                     cleanup();
  529.                 }
  530.                 strcpy(aptr->hilo,tmp);
  531.             }
  532.             if (t_status == ':') sub_count++;
  533.             aptr->status = t_status;
  534.             aptr->position = newsrc_order++;
  535.             i_active++;
  536.             if (i_active == c_active) i_active = 0;
  537.             continue;
  538.         }
  539.  
  540.         low = 0;
  541.         high = c_active - 1;
  542. loop2:
  543.         if (low <= high) {
  544.             mid = (low+high)/2;
  545.             aptr = act[mid];
  546.             result = strcmp(buffer,aptr->name);
  547.             if (result == 0) {
  548.                 if (strlen(tmp) > 0) {
  549.                     if ((aptr->hilo = (char *)wallop(strlen(tmp)+1)) 
  550.                         == (char *)NULL) {
  551.                         printf("Error allocating memory!\n");
  552.                         return;
  553.                     }
  554.                     strcpy(aptr->hilo,tmp);
  555.                 }
  556.                 if (t_status == ':') sub_count++;
  557.                 aptr->status = t_status;
  558.                 aptr->position = newsrc_order++;
  559.                 i_active = mid + 1;
  560.             /* This next hack is necessary to prevent the
  561.             corner case where mid has pointed at the very
  562.             last entry in the act strucutre. */
  563.                 if (i_active == c_active)  i_active = 0;
  564.                 continue; /* read loop */
  565.             } else
  566.             if (result > 0) { /* after */
  567.                 low = mid+1;
  568.                 goto loop2; 
  569.             } else
  570.             if (result < 0) { /* before */
  571.                 high = mid-1;
  572.                 goto loop2; 
  573.             }
  574.         } else {
  575.             bog_count++;    /* must be bogus! */
  576.  
  577.             if ((aptr = (struct actif *) wallop(sizeof(struct actif)))
  578.                 == (struct actif *)NULL) {
  579.                 printf("Fatal error while allocating memory!\n");
  580.                 cleanup();
  581.             }
  582.             act[c_active] = aptr; 
  583.  
  584.             if ((aptr->name = (char *)wallop(strlen(buffer)+1)) 
  585.                == (char *)NULL) {
  586.                 printf("Fatal error while allocating memory!\n");
  587.                 cleanup();
  588.             }
  589.             strcpy(aptr->name,buffer);
  590.             if (verbose) {
  591.                 printf("Bogus newsgroup: %s\n",aptr->name);
  592.             }
  593.  
  594.             if (strlen(tmp) > 0) {
  595.                 if ((aptr->hilo = (char *)wallop(strlen(tmp)+1)) 
  596.                    == (char *)NULL) {
  597.                     printf("Fatal error while allocating memory!\n");
  598.                     cleanup();
  599.                 }
  600.                 strcpy(aptr->hilo,tmp);
  601.             }
  602.             aptr->hi = 0;
  603.             aptr->mark = 0;
  604.             aptr->desc = "Bogus newsgroup (not in active file)";
  605.             aptr->flag = '\0';
  606.             if (t_status == ':') sub_count++;
  607.             aptr->status = t_status;
  608.             aptr->position = newsrc_order++;
  609.             aptr->depth = (struct actif *) NULL;
  610.             c_active++;
  611.             if (c_active < 2) continue;
  612.             qsort( act, (unsigned) c_active,(unsigned) sizeof(act[0]), 
  613.                qcompare);
  614.             continue; /* with read loop */
  615.         }
  616.     }
  617.     fclose(fnewsrc);
  618.  
  619.     if ((bog_count > 0) && (verbose)) {
  620.         if (bog_count == 1)
  621.             printf("There was 1 bogus newsgroup in your .newsrc.\n");
  622.         else
  623.             printf("There were %d bogus newsgroups in your .newsrc.\n",
  624.                 bog_count);
  625.         warning = TRUE;
  626.     }
  627.     if ((sub_count > 0) && (verbose)) {
  628.         printf("There were %d subscribed newsgroups in your .newsrc.\n",
  629.                 sub_count);
  630.         warning = TRUE;
  631.     }
  632.  
  633.  
  634.     /* Now let's sort this lot into the order that we originally
  635.     read it from the .newsrc in.  New newsgroups will be forced
  636.     to the bottom, making it easier for them to be spotted.  */
  637.  
  638.     qsort( act, (unsigned) c_active, (unsigned) sizeof(act[0]), icompare);
  639.  
  640. #ifdef UNIX
  641.     if (home != (char *)NULL)
  642.         sprintf(tmp, "%s/.newsrc.eep", home);
  643.     else    sprintf(tmp, ".newsrc.eep");
  644.  
  645.     if ((fnewsrc = fopen(tmp, "w")) == (FILE *)NULL) {
  646.        printf("warning: cannot create .newsrc.eep -- check permissions\n");
  647.        warning = TRUE;
  648.     }
  649. #endif /* UNIX */
  650.  
  651. #ifdef NNTP
  652.     if (nntp_ok) {
  653.         close_server();
  654.         if (verbose) printf("NNTP connection closed\n");
  655.     }
  656. #endif /* NNTP */
  657.  
  658.     if (warning) {
  659.         printf("\nPress ENTER to continue.");
  660.         gets(buffer);
  661.     }
  662. }
  663.  
  664. #ifdef NNTP
  665. /* These includes must come from your NNTP client directory. */
  666. #include <conf.h>
  667. #include <nntp.h>
  668. #include <clientlib.h>
  669.  
  670. /* init_nntp() -- this routine will try to connect with the NNTP
  671.  *                or INND server.  It will return -1 if no contact
  672.  *                is made.
  673.  */
  674.  
  675. int   init_nntp()
  676. {
  677. long retcode = 0;
  678. char *newsserver;
  679. int counter = 0;
  680.  
  681. /* find out who our NNTP server is */
  682.  
  683.     newsserver = getserverbyfile (SERVER_FILE);
  684.  
  685. /* Complain if we don't know who it is */
  686.  
  687.     if (!newsserver) {
  688.         (void) fprintf (stderr, "Can't find a news server.\n");
  689.         (void) fprintf (stderr,
  690.             "Set your NNTPSERVER variable to name of server.\n");
  691.         return(-1);
  692.     }
  693.  
  694.     /* Connect to NNTP server.  Print error if we can't */
  695.  
  696.     retcode = server_init (newsserver);
  697.     if (retcode == (-1)) return(-1);
  698.  
  699.     handle_server_response(retcode, newsserver);
  700.  
  701.     if (verbose) printf("Connected to NNTP server %s\n",newsserver);
  702.     return(0);
  703. }
  704.  
  705.  
  706. /* This routine will obtain the newsgroup descriptions from the NNTP
  707.  * server, matching them with the active file.  If there are any
  708.  * problems, this routine will return -1.
  709.  */
  710.  
  711. int    read_desc_nntp()
  712. {
  713. char    *name, *desc;  /* pointers to name and description */
  714. int     counter = 0;
  715.  
  716.     if (verbose) 
  717.         printf("Obtaining newsgroup descriptions from news server\n");
  718.  
  719.     i_active = 0;      /* start with first one of course */
  720.     aptr = (struct actif *)NULL;
  721.     (void) sprintf(buffer, "list newsgroups");
  722.     put_server (buffer);
  723.     get_server(buffer,BUFSIZE);  /* first line is result code */
  724.  
  725.     while (get_server(buffer, BUFSIZE) >= 0) {
  726.  
  727.     /* ignore comment lines or blank lines */
  728.         switch(buffer[0]) {
  729.         case '#':
  730.         case '\n':
  731.         case '\r':
  732.         case '\0':
  733.             continue;
  734.         }
  735.  
  736.     /* NNTP server finishes with a line with "."  */
  737.         if (buffer[0] == '.') break;
  738.  
  739.     /* Get name and description.  If either is absent, skip this line. */
  740.         if ((name = strtok(buffer, " \t\r\n")) == (char *)NULL) continue;
  741.         if ((desc = strtok((char *)NULL, "\r\n")) == (char *)NULL) continue;
  742.  
  743.     /* Advance over any whitespace preceding description */
  744.         while ((*(desc) == ' ') || (*(desc) == '\t')) desc++;
  745.  
  746.     /* Although we cannot assume that the newsgroups are in alphabetical
  747.      * order, we'll try looking at the next one anyway before doing a
  748.      * search. */
  749.  
  750.         if (i_active <= c_active)    /* range check */
  751.             aptr = act[i_active];
  752.  
  753.     /* This next line should never happen.  It would only occur if
  754.      * we've run out of things to match! */
  755.  
  756.         if (aptr == (struct actif *)NULL) {
  757.             if (verbose) printf("This should never happen!\n");
  758.             continue;
  759.         }
  760.  
  761.     /* Now compare the name read with current position in active file */
  762.  
  763.         if (strcmp(name,aptr->name) == 0) {
  764.  
  765. /* Here we look for the best description possible, i.e. one that is not 
  766. null, and preferably longest (i.e. not just a '?').  This will be because 
  767. we may find duplicate lines in the newsgroups file.  Also, it may be 
  768. possible to find no description at all. */
  769.  
  770.             if (aptr->desc != (char *)NULL) {
  771.             /* don't accept an inferior description */
  772.                 if (strlen(desc) <= strlen(aptr->desc)) continue;
  773.             }
  774.  
  775.         /* allocate space for string + null byte, then copy it in */
  776.             if ((aptr->desc = (char *)wallop(strlen(desc)+1)) == (char *)NULL) {
  777.                 printf("Fatal error while allocating memory!\n");
  778.                 cleanup();
  779.             }
  780.             strcpy(aptr->desc,desc);
  781.             counter++;
  782.             i_active++;      /* advance index to next pointer */
  783.             if (i_active == c_active) i_active = 0;
  784.             continue;
  785.         }
  786.  
  787.     /*  Here we begin a binary chop search, comparing the newsgroup
  788.      *  name we have just read from the newsgroups file with the list
  789.      *  of newsgroups read from the active file.  */
  790.  
  791.         low = 0;
  792.         high = c_active - 1;
  793. loop1:
  794.         if (low <= high) {
  795.             mid = (low+high)/2;
  796.             aptr = act[mid];
  797.             result = strcmp(name,aptr->name);
  798.             if (result == 0) {
  799.  
  800.                 if (aptr->desc != (char *)NULL) {
  801.                 /* don't accept an inferior description */
  802.                     if (strlen(desc) <= strlen(aptr->desc)) continue;
  803.                 }
  804.  
  805.             /* allocate space for string + null byte, then copy it in */
  806.                 if ((aptr->desc = (char *)wallop(strlen(desc)+1))
  807.                    == (char *)NULL) {
  808.                     printf("Fatal error while allocating memory!\n");
  809.                     cleanup();
  810.                 }
  811.                 strcpy(aptr->desc,desc);
  812.  
  813.                 i_active = mid + 1;
  814.                 if (i_active == c_active) i_active = 0;
  815.                 continue; /* with read loop */
  816.             } else
  817.             if (result > 0) { /* after */
  818.                 low = mid+1;
  819.                 goto loop1;
  820.             } else
  821.             if (result < 0) { /* before */
  822.                 high = mid-1;
  823.                 goto loop1;
  824.             }
  825.         }
  826.     }
  827.     if (counter == 0) return(-1);
  828.     if (verbose) printf("Found %d descriptions.\n",counter);
  829.     return(0);
  830. }
  831.  
  832. /* This routine will obtain the active file from the NNTP server. 
  833.  * Any problems will causes this routine to return -1. 
  834.  */
  835.  
  836. int    read_active_nntp()
  837. {
  838.     if (verbose) printf("Requesting active file from news server\n");
  839.  
  840.     (void) sprintf(buffer, "list active");
  841.     put_server (buffer);
  842.     get_server(buffer,BUFSIZE);  /* first line is result code */
  843.     c_active = 0;    /* count the newsgroups as we go. */
  844.  
  845.     while (get_server(buffer, BUFSIZE) >= 0) {
  846.     /* ignore comment lines or blank lines */
  847.         switch(buffer[0]) {
  848.         case '#':
  849.         case '\n':
  850.         case '\r':
  851.         case '\0':
  852.             continue;
  853.         }
  854.         if (buffer[0] == '.') break;
  855.  
  856.     /* strip off newlines or returns */
  857.         while ((ptr = strchr(buffer,'\n')) != (char *)NULL) *ptr = '\0';
  858.         while ((ptr = strchr(buffer,'\r')) != (char *)NULL) *ptr = '\0';
  859.  
  860.     /* process line from active file -- low message number ignored. */
  861.         if ((name = strtok(buffer, " \t\r\n")) == (char *)NULL) continue;
  862.         if ((hi   = strtok((char *)NULL, " \t\r\n")) == (char *)NULL) continue;
  863.         if ((lo   = strtok((char *)NULL, " \t\r\n")) == (char *)NULL) continue;
  864.         if ((flag = strtok((char *)NULL, " \t\r\n")) == (char *)NULL) continue;
  865.  
  866.     /* check validity of data.  Must have something in each position. */
  867.  
  868.     /* Now allocate a chunk of memory for actif */
  869.         if ((aptr = (struct actif *) wallop(sizeof(struct actif)))
  870.             == (struct actif *)NULL) {
  871.             printf("Fatal error while allocating memory!\n");
  872.             cleanup();
  873.         }
  874.         act[c_active] = aptr; /* record this pointer */
  875.  
  876.     /* allocate space for string + null byte, then copy it in */
  877.         if ((aptr->name = (char *)wallop(strlen(name)+1)) == (char *)NULL) {
  878.             printf("Fatal error while allocating memory!\n");
  879.             cleanup();
  880.         }
  881.  
  882.     /* now move data into the structure */
  883.         strcpy(aptr->name,name);
  884.         aptr->hi = atol(hi);
  885.         aptr->hilo = (char *)NULL;
  886.         aptr->desc = (char *)NULL;
  887.         aptr->mark = 0;            /* mark flag */
  888.         aptr->flag = flag[0];      /* from active file */
  889.         aptr->status = '!';        /* from .newsrc */
  890.         aptr->position = 9999;        /* sort unknowns at end */
  891.         aptr->depth = (struct actif *) NULL;
  892.         c_active++;
  893.     }
  894.     if (c_active == 0) {
  895.         printf("Fatal: News server didn't have any active newsgroups!\n");
  896.         cleanup();
  897.     }
  898.  
  899.     if (verbose) {
  900.         printf("There were %d newsgroups in the active file.\n", c_active);
  901.         warning = TRUE;
  902.     }
  903. }
  904. #endif /* NNTP */
  905.  
  906. /* This routine will read the active file which forms part of
  907.  * the news system to determine which newsgroups are valid.
  908.  */
  909.  
  910. int   read_active_file()
  911. {
  912.     c_active = 0;    /* count the newsgroups as we go. */
  913.  
  914.     if ((factive = fopen(ACTIVEFILE, "r")) == (FILE *)NULL) {
  915.         printf("Fatal: Unable to read %s\n",ACTIVEFILE);
  916.         cleanup();
  917.     }
  918.  
  919.     if (verbose) printf("Opening active file %s\n",ACTIVEFILE);
  920.     while (fgets(buffer,BUFSIZE,factive) != (char *)NULL) {
  921.  
  922.     /* ignore comment lines or blank lines */
  923.         switch(buffer[0]) {
  924.         case '#':
  925.         case '\n':
  926.         case '\r':
  927.         case '\0':
  928.             continue;
  929.         }
  930.  
  931.     /* strip off newlines or returns */
  932.         while ((ptr = strchr(buffer,'\n')) != (char *)NULL) *ptr = '\0';
  933.         while ((ptr = strchr(buffer,'\r')) != (char *)NULL) *ptr = '\0';
  934.  
  935.     /* process line from active file -- low message number ignored. */
  936.         if ((name = strtok(buffer, " \t\r\n")) == (char *)NULL) continue;
  937.         if ((hi   = strtok((char *)NULL, " \t\r\n")) == (char *)NULL) continue;
  938.         if ((lo   = strtok((char *)NULL, " \t\r\n")) == (char *)NULL) continue;
  939.         if ((flag = strtok((char *)NULL, " \t\r\n")) == (char *)NULL) continue;
  940.  
  941.     /* check validity of data.  Must have something in each position. */
  942.  
  943.     /* Now allocate a chunk of memory for actif */
  944.         if ((aptr = (struct actif *) wallop(sizeof(struct actif)))
  945.             == (struct actif *)NULL) {
  946.             printf("Fatal error while allocating memory!\n");
  947.             cleanup();
  948.         }
  949.         act[c_active] = aptr; /* record this pointer */
  950.  
  951.     /* allocate space for string + null byte, then copy it in */
  952.         if ((aptr->name = (char *)wallop(strlen(name)+1)) == (char *)NULL) {
  953.             printf("Fatal error while allocating memory!\n");
  954.             cleanup();
  955.         }
  956.  
  957.     /* now move data into the structure */
  958.         strcpy(aptr->name,name);
  959.         aptr->hi = atol(hi);
  960.         aptr->hilo = (char *)NULL;
  961.         aptr->desc = (char *)NULL;
  962.         aptr->mark = 0;            /* mark flag */
  963.         aptr->flag = flag[0];      /* from active file */
  964.         aptr->status = '!';        /* from .newsrc */
  965.         aptr->position = 9999;     /* sort unknowns at end */
  966.         aptr->depth = (struct actif *) NULL;
  967.         c_active++;
  968.     }
  969.     fclose(factive);
  970.     if (c_active == 0) {
  971.         printf("Fatal: Can't find any news groups in %s\n",ACTIVEFILE);
  972.         cleanup();
  973.     }
  974.  
  975.     if (verbose) {
  976.         printf("There were %d newsgroups in the active file.\n", c_active);
  977.         warning = TRUE;
  978.     }
  979. }
  980.  
  981. /***********************
  982.  *  MAIN STARTS HERE   *
  983.  ***********************/
  984.  
  985. main(argc, argv)
  986. int    argc;
  987. char    **argv;
  988. {
  989. int    ch;
  990. int    timer;
  991.  
  992. #ifdef UNIX
  993.     while ((ch = getopt(argc,argv,"dnpv!?")) != EOF) switch(ch) {
  994.         case 'd':
  995.             del_unsub = TRUE;
  996.             if (verbose) printf("Unsubscribed newsgroups will be deleted\n");
  997.             break;
  998.         case 'p':
  999.             eepoint++;
  1000.             if (verbose) printf("Terse pointer enabled\n");
  1001.             break;
  1002.         case 'v':
  1003.             if (verbose)
  1004.                 verbose = FALSE;
  1005.             else {
  1006.                 verbose = TRUE;
  1007.                 printf("Verbose messages enabled\n");
  1008.             }
  1009.             break;
  1010. #ifdef NNTP
  1011.         case 'n':   /* DISABLE NNTP */
  1012.             nntp_ok = FALSE;
  1013.             if (verbose) printf("NNTP connection disabled.\n");
  1014.             break;
  1015. #endif /* NNTP */            
  1016.         case '?':
  1017.             fprintf(stderr,"usage: eep [-d] [-n] [-p] [-v]\n\n");
  1018.             fprintf(stderr,"-d delete unsubscribed newsgroups\n");
  1019.             fprintf(stderr,"-n disable NNTP connection\n");
  1020.             fprintf(stderr,"-p terse pointer toggle\n");
  1021.             fprintf(stderr,"-v verbose mode toggle\n");
  1022.             fprintf(stderr,"\nUse man eep for more info.\n");
  1023.             exit(0);
  1024.     }
  1025. #endif /* UNIX */
  1026.  
  1027.     fprintf(stderr,"EEP! version 1.9 .newsrc editor\n");
  1028. #ifdef NNTP
  1029.     if (verbose) printf("NNTP supported\n");
  1030.     if (nntp_ok && (init_nntp() == 0)) {
  1031.         nntp_ok = TRUE;
  1032.         if (verbose) printf("NNTP connection established\n");
  1033.     } else
  1034.         if (verbose) printf("NNTP connection NOT established\n");
  1035. #endif /* NNTP */
  1036.     initial();    /* read in newsgroups, active and .newsrc */
  1037.     newsmain();
  1038.     cleanup();
  1039. } /* end of eepmain.c */
  1040.